<?php

declare(strict_types=1);

namespace DreamCat\StateMachine\Parser\Impl;

use DreamCat\StateMachine\Error\DefineConfigException;
use DreamCat\StateMachine\Parser\ConfigParserInterface;
use DreamCat\StateMachine\Parser\Inner\ParseConfig;
use DreamCat\StateMachine\Popo\MachineDefine;
use stdClass;

/**
 * 按目录解析的配置解析器
 * @author vijay
 */
class DirConfigParser implements ConfigParserInterface
{
    /** @var ParseConfig 辅助类 */
    private ParseConfig $parseConfig;
    /** @var string 定义schema的目录 */
    private string $schemaDir;

    /**
     * @param string $configDir 配置文件所有的目录
     */
    public function __construct(private string $configDir)
    {
        $this->parseConfig = new ParseConfig();
        $this->schemaDir = $this->parseConfig->getSchemaDir();
    }

    /**
     * @inheritDoc
     */
    public function parseConfig(): MachineDefine
    {
        if (!is_dir($this->configDir)) {
            throw new DefineConfigException("指定的配置目录 {$this->configDir} 不存在");
        }
        if (!is_readable($this->configDir)) {
            throw new DefineConfigException("指定的配置目录 {$this->configDir} 不可读取"); // @codeCoverageIgnore
        }
        $parseConfig = $this->readBasic();
        $parseConfig->roles = $this->scanDirJsonFiles("roles", "role", "角色");
        $parseConfig->actions = $this->scanDirJsonFiles("actions", "action", "动作");
        $parseConfig->nodes = $this->scanDirJsonFiles("nodes", "node", "节点");
        return $this->parseConfig->checkParseConfig($parseConfig);
    }

    /**
     * 读取基本配置
     * @return stdClass 生成的配置
     * @throws DefineConfigException
     */
    private function readBasic(): stdClass
    {
        $parseConfig = new stdClass();
        $basic = $this->parseConfig->getJsonFile("{$this->configDir}/basic.json", "状态机基础配置");
        $schema = $this->parseConfig->getJsonFile("{$this->schemaDir}/basic.json", "基础配置定义");
        $this->parseConfig->validSchema($basic, $schema, "状态机基础配置");
        $parseConfig->machineFactory = $basic->machineFactory;
        return $parseConfig;
    }

    /**
     * 扫描目录下的所有json
     * @param string $dirName 目前的相对地址
     * @param string $schemaName 定义结构的文件名，不包括扩展名
     * @param string $typeName 目录下的功能
     * @return stdClass[] 解析后的数据列表
     * @throws DefineConfigException
     */
    private function scanDirJsonFiles(string $dirName, string $schemaName, string $typeName): array
    {
        $dir = "{$this->configDir}/{$dirName}";
        if (!is_dir($dir)) {
            throw new DefineConfigException("{$typeName}列表目录 {$dir} 不存在");
        }
        if (!is_readable($dir)) {
            throw new DefineConfigException("{$typeName}列表目录 {$dir} 无法读取"); // @codeCoverageIgnore
        }
        $schema = $this->parseConfig->getJsonFile("{$this->schemaDir}/{$schemaName}.json", "{$typeName}配置定义");
        $list = [];
        foreach (scandir($dir) as $file) {
            if ($file == "." || $file == "..") {
                continue;
            }
            if (!str_ends_with($file, ".json")) {
                continue;
            }
            $desc = $typeName . substr($file, 0, -4) . " 配置";
            $config = $this->parseConfig->getJsonFile("{$dir}/{$file}", $desc);
            $this->parseConfig->validSchema($config, $schema, $desc);
            $list[] = $config;
        }
        return $list;
    }
}

# end of file
